﻿using Cache.Services.Interface;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Cache.Services.Implement
{
    /// <summary>
    /// Redis缓存实现
    /// </summary>
    public sealed class RedisCacheContext : ICacheContext
    {

        private static ConnectionMultiplexer Conn { get; set; }
        private static IDatabase RedisDb { get; set; }
        //系统自定义Key前缀
        private readonly string _sysCustomKey = "caches:";

        /// <summary>
        /// 初始化连接
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="db">默认DB</param>
        public static void Init(string conn, int db = 0)
        {
            Conn = ConnectionMultiplexer.Connect(conn);
            RedisDb = Conn.GetDatabase(db);
        }
        /// <summary>
        /// RedisCacheContext
        /// </summary>
        public RedisCacheContext()
        {

        }

        /// <summary>
        /// 是否存在此缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override bool Exists(string key)
        {
            return RedisDb.KeyExists(key);
        }

        /// <summary>
        /// 获取缓存对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public override T GetCache<T>(string key)
        {
            RedisValue value = RedisDb.StringGet(key);
            if (!value.HasValue)
                return default;
            if (typeof(T) == typeof(string))
                return (T)Convert.ChangeType(value, typeof(T));
            else
                return JsonConvert.DeserializeObject<T>(value);
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expire"></param>
        public override bool SetCache<T>(string key, T value, DateTime expire)
        {
            if (typeof(T) == typeof(string))
            {
                return RedisDb.StringSet(key, value.ToString(), expire - DateTime.Now);
            }
            else
            {
                return RedisDb.StringSet(key, JsonConvert.SerializeObject(value), expire - DateTime.Now);
            }
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override bool RemoveCache(string key)
        {
            return RedisDb.KeyDelete(key);
        }
        /// <summary>
        /// 重命名Key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="newKey"></param>
        /// <returns></returns>
        public override bool RenameKey(string key, string newKey)
        {
            return RedisDb.KeyRename(key, newKey);
        }


        #region Redis缓存扩展

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        public async Task<bool> SortedSetAddAsync<T>(string key, T value, double score)
        {
            key = AddSysCustomKey(key);
            var data = JsonConvert.SerializeObject(value);
            return await RedisDb.SortedSetAddAsync(key, data, score);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async Task<bool> SortedSetRemoveAsync<T>(string key, T value)
        {
            key = AddSysCustomKey(key);
            var data = JsonConvert.SerializeObject(value);
            return await RedisDb.SortedSetRemoveAsync(key, data);
        }

        /// <summary>
        /// 获取全部
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<T>> SortedSetRangeByRankAsync<T>(string key)
        {
            key = AddSysCustomKey(key);
            var values = await RedisDb.SortedSetRangeByRankAsync(key);
            return ConvetList<T>(values);
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> SortedSetLengthAsync(string key)
        {
            key = AddSysCustomKey(key);
            return await RedisDb.SortedSetLengthAsync(key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="oldKey"></param>
        /// <returns></returns>
        private string AddSysCustomKey(string oldKey)
        {
            var prefixKey = _sysCustomKey;
            return prefixKey + oldKey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        private List<T> ConvetList<T>(RedisValue[] values)
        {
            List<T> result = new List<T>();
            foreach (var item in values)
            {
                var model = ConvertObj<T>(item);
                result.Add(model);
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private static T ConvertObj<T>(RedisValue value)
        {
            if (value.HasValue)
            {
                if (typeof(T).Name.Equals(typeof(string).Name))
                {
                    return JsonConvert.DeserializeObject<T>($"'{value}'");
                }
                else
                {
                    T t = JsonConvert.DeserializeObject<T>(value);
                    if (t == null)
                    {
                        throw new Exception("Convert fail");
                    }
                    return t;
                }
            }
            return default;
        }

        #endregion
    }
}
